/*
 * OptInvocation.c
 *
 *  Created on: 2011-7-10
 *      Author: gexiao
 */
/*
 * Utility functions for managing an invocation of "dexopt"
 */
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <stdbool.h>
#include <errno.h>
#include "OptInvocation.h"
#include "DexFile.h"

static const char* kClassesDex = "classes.dex";

/*
 * Given the filename of a .jar or .dex file, construct the DEX file cache name.
 *
 * For a Jar, "subFileName" is the name of the entry (usually "classes.dex").
 * For a DEX, it may be NULL.
 *
 * Returns "false" if something goes wrong.
 */
int dexOptGenerateCacheFileName(const char* fileName, const char* subFileName,
		char* nameBuf, unsigned int bufSize) {
	static const char kDexCachePath[] = "dalvik-cache";
	char absoluteFile[bufSize];
	const size_t kBufLen = bufSize - 1;
	const char* dataRoot;
	char* cp;

	/*
	 * Get the absolute path of the Jar or DEX file.
	 */
	absoluteFile[0] = '\0';
	if (fileName[0] != '/') {
		/*
		 * Generate the absolute path. This doesn't do everything it
		 * should, e.g. if filename is "./out/whatever" it doesn't crunch
		 * the leading "./" out, but it'll do.
		 */
		if (getcwd(absoluteFile, kBufLen) == NULL) {
			return false;
		}
		strncat(absoluteFile,"/",kBufLen);
	}
	strncat(absoluteFile,fileName,kBufLen);

	/*
	 * Append the name of the Jar file entry, if any. This is not currently
	 * required, but will be if we start putting more than one DEX file
	 * in a Jar.
	 */
	if (subFileName != NULL) {
		strncat(absoluteFile,"/",kBufLen);
		strncat(absoluteFile,subFileName,kBufLen);
	}

	/*
	 * Turn the path into a flat filename by replacing
	 * any slashes after the first one with '@' characters.
	 */
	cp = absoluteFile + 1;
	while (*cp != '\0') {
		if (*cp == '/') {
			*cp = '@';
		}
		cp++;
	}

	/*
	 * Build the name of the cache directory.
	 */
	dataRoot = getenv("ANDROID_DATA");
	if (dataRoot == NULL)
		dataRoot = "/data";

	/*
	 * Tack on the file name for the actual cache file path.
	 */strncat(nameBuf,absoluteFile,kBufLen);

	 return true;
}

/*
 * Create a skeletal "opt" header in a new file. Most of the fields are
 * initialized to garbage, but we fill in "dexOffset" so others can
 * see how large the header is.
 *
 * "fd" must be positioned at the start of the file. On return, it will
 * be positioned just past the header, and the place where the DEX data
 * should go.
 *
 * Returns 0 on success, errno on failure.
 */
int dexOptCreateEmptyHeader(int fd) {
	DexOptHeader optHdr;

	ssize_t actual;

	assert(lseek(fd, 0, SEEK_CUR) == 0);

	/*
	 * The data is only expected to be readable on the current system, so
	 * we just write the structure. We do need the file offset to be 64-bit
	 * aligned to fulfill a DEX requirement.
	 */memset(&optHdr,0xff,sizeof(optHdr));
	optHdr.dexOffset = sizeof(optHdr);
	actual = write(fd, &optHdr, sizeof(optHdr));
	if (actual != sizeof(optHdr)) {
		int err = errno ? errno : -1;
		return errno;
	}
	return 0;
}
